﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using Dapper.ExpressionExtension.ExpressionMappers;
using Dapper.ExpressionExtension.SqlBuilders;

namespace Dapper.ExpressionExtension.Resolvers
{
    /// <summary>
    /// 解析器帮助类
    /// </summary>
    public static class ResolverHelper
    {
        private static readonly ConcurrentDictionary<string, string> TypeTableNameCache = new ConcurrentDictionary<string, string>();
        private static readonly ConcurrentDictionary<string, string> ColumnNameCache = new ConcurrentDictionary<string, string>();
        private static readonly ConcurrentDictionary<Type, ColumnPropertyInfo[]> TypeKeyPropertiesCache = new ConcurrentDictionary<Type, ColumnPropertyInfo[]>();
        private static readonly ConcurrentDictionary<Type, ColumnPropertyInfo[]> TypePropertiesCache = new ConcurrentDictionary<Type, ColumnPropertyInfo[]>();

        internal static IPropertyResolver PropertyResolver = new DefaultPropertyResolver();
        internal static IKeyPropertyResolver KeyPropertyResolver = new DefaultKeyPropertyResolver();
        internal static ITableNameResolver TableNameResolver = new DefaultTableNameResolver();
        internal static IColumnNameResolver ColumnNameResolver = new DefaultColumnNameResolver();

        /// <summary>
        /// 设置属性解析器
        /// </summary>
        /// <param name="resolver"></param>
        public static void SetPropertyResolver(IPropertyResolver resolver) => PropertyResolver = resolver;

        /// <summary>
        /// 设置主键解析器
        /// </summary>
        /// <param name="resolver"></param>
        public static void SetKeyPropertyResolver(IKeyPropertyResolver resolver) => KeyPropertyResolver = resolver;

        /// <summary>
        /// 设置表名解析器
        /// </summary>
        /// <param name="resolver"></param>
        public static void SetTableNameResolver(ITableNameResolver resolver) => TableNameResolver = resolver;

        /// <summary>
        /// 设置列名解析器
        /// </summary>
        /// <param name="resolver"></param>
        public static void SetColumnNameResolver(IColumnNameResolver resolver) => ColumnNameResolver = resolver;

        /// <summary>
        /// 获取主键属性列表
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static ColumnPropertyInfo[] KeyProperties(Type type)
        {
            if (TypeKeyPropertiesCache.TryGetValue(type, out var keyProperties))
            {
                return keyProperties;
            }
            keyProperties = KeyPropertyResolver.ResolveKeyProperties(type);
            TypeKeyPropertiesCache.TryAdd(type, keyProperties);
            return keyProperties;
        }

        /// <summary>
        /// 获取属性列表
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static IEnumerable<ColumnPropertyInfo> Properties(Type type)
        {
            if (!TypePropertiesCache.TryGetValue(type, out var properties))
            {
                properties = PropertyResolver.ResolveProperties(type).ToArray();
                TypePropertiesCache.TryAdd(type, properties);
            }

            return properties;
        }

        /// <summary>
        /// 获取表名
        /// </summary>
        /// <param name="type"></param>
        /// <param name="connection"></param>
        /// <returns></returns>
        public static string Table(Type type, IDbConnection connection) =>
            Table(type, ExpressionMapper.GetSqlBuilder(connection));

        /// <summary>
        /// 获取表名
        /// </summary>
        /// <param name="type"></param>
        /// <param name="sqlBuilder"></param>
        /// <returns></returns>
        public static string Table(Type type, ISqlBuilder sqlBuilder)
        {
            var key = $"{sqlBuilder.GetType()}.{type}";
            if (TypeTableNameCache.TryGetValue(key, out var name))
            {
                return name;
            }
            var tableName = TableNameResolver.ResolveTableName(type);
            name = string.Join(".", tableName.Split('.').Select(sqlBuilder.QuoteIdentifier));
            TypeTableNameCache.TryAdd(key, name);
            return name;
        }

        /// <summary>
        /// 获取列名
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <param name="connection"></param>
        /// <returns></returns>
        public static string Column(PropertyInfo propertyInfo, IDbConnection connection)
            => Column(propertyInfo, ExpressionMapper.GetSqlBuilder(connection));

        /// <summary>
        /// 获取列名
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <param name="sqlBuilder"></param>
        /// <returns></returns>
        public static string Column(PropertyInfo propertyInfo, ISqlBuilder sqlBuilder)
        {
            var key = $"{sqlBuilder.GetType()}.{propertyInfo.DeclaringType}.{propertyInfo.Name}";
            if (ColumnNameCache.TryGetValue(key, out var columnName))
            {
                return columnName;
            }
            columnName = sqlBuilder.QuoteIdentifier(ColumnNameResolver.ResolveColumnName(propertyInfo));
            ColumnNameCache.TryAdd(key, columnName);
            return columnName;
        }
    }
}
